package com.woniuxy.interceptor.config;

import com.woniuxy.exception.handle.HandlerAccessDeniedException;
import com.woniuxy.filter.CustomAuthenticationFilter;
import com.woniuxy.service.impl.CustomUserDetailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * springsecurity的主配置类
 * 1） 配置用户信息源：内存  数据库
 * 2） 权限的控制（大范围）
 */

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启security的权限注解功能
public class CustomSpringSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 该方法用于配置用户信息来自哪里：内存 或 数据库
     * 注入自定义的UserDetailService，通过loadUserByUsername方法获取到用户的角色与权限信息
     * （或者直接注入CustomUserDetailService也可以，CustomUserDetailService实现了UserDetailsService接口）
     */

    @Resource
    private CustomUserDetailService customUserDetailService;
//    private UserDetailsService userDetailsService;

    // 注入CustomAuthenticationFilter
    @Resource
    private CustomAuthenticationFilter customAuthenticationFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        /** 内存
         *  注意：内存和数据库不要同时开启，否则会出错
         * */
        // 配置两个账号与密码
//        auth.inMemoryAuthentication()
//                .withUser("zhangsan")
//                // {noop}告诉编码器密码不加密
//                .password("{noop}123")
//                .roles("teacher", "admin")
//                .and()
//                .withUser("wangwu")
//                .password("{noop}111")
//                .roles("student");

        /** 数据库
         *  设置数据源、密码编译器
         * */
//        auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
        auth.userDetailsService(customUserDetailService).passwordEncoder(encoder());
    }

    /** 该方法用于权限的控制（大范围），不如注解精细 */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    // 访问user开头的必须是admin角色
//                .antMatchers("/user/**").hasRole("admin")
                .antMatchers("/auth/login").anonymous() // 只允许匿名访问
//                .antMatchers("/exam/**").hasRole("student")
                    // 其他的都能直接访问（无需登录）
                    .anyRequest().permitAll()
                    .and()
                    // 1. 开启异常处理
                    .exceptionHandling()
                    // 2. 处理被拒绝请求的异常
                    .accessDeniedHandler(new HandlerAccessDeniedException())
                    // 添加form表单支持，因为配置了url，默认不再支持form表单登录，不会显示登录页面
                    /** 开启SpringSecurity的跨域 */
                    .and().cors()
                    .and().formLogin()
                    /** 将customAuthenticationFilter放在判断上下文是否有authentication对象的过滤器 UsernamePasswordAuthenticationFilter之前 */
                    .and().addFilterBefore(customAuthenticationFilter,UsernamePasswordAuthenticationFilter.class)
                    .csrf().disable(); // 关闭跨站伪造防御  PostMan也算是跨站伪造请求
        }

        /**
         * 指定密码编码器
         * */
        @Bean
        public PasswordEncoder encoder() {
            return new BCryptPasswordEncoder();
        }

        // AuthenticationManager 是一个接口，需要告诉security（配置类中）去创建authenticationManager对象
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
    }
}
